﻿#region namespaces

using System;
using System.ComponentModel;
using System.Xml.Linq;
using ZOIL.DataBackend.BaseX.Annotations;
using ZOIL.DataModel;

#endregion

namespace ZOIL.DataBackend.BaseX
{
    //TODO: Nur Sachen einstellbar machen die auch einstellbar sein sollen
    public class DatabaseConfiguration
    {
        #region ctor

        public DatabaseConfiguration()
        {
            Host = "localhost";
            Port = 1984;
            Username = "admin";
            Password = "admin";
            DatabaseName = "Zoil";
            EventName = "ZoilChanged";
            UseUpdIndex = true;

            IdCounterNamespace = "http://zoil.codeplex.com/schemas/2012/idcounter";
            IdCounterDocumentAndTagName = "IdCounter";

            PathNamespace = "http://zoil.codeplex.com/schemas/2012";

            Serializer = new XmlSerializer();

            AcceptRemotelyCreatedObjectCondition = _ => false;
            CommitToDatabaseThrottleInterval = TimeSpan.FromMilliseconds(500);
        }

        #endregion

        #region Connection

        public string Host { get; set; }
        public int Port { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string DatabaseName { get; set; }
        public string EventName { get; set; }
        public bool UseUpdIndex { get; set; }

        #endregion

        #region Id

        public string IdCounterNamespace { get; set; }
        public string IdCounterDocumentAndTagName { get; set; }

        internal string GetIdCounterNamespaceDeclaration(string prefix)
        {
            return string.Format("declare namespace {0} = \"{1}\";\n", prefix, IdCounterNamespace);
        }

        #region IdNames

        private static readonly Lazy<string> _databaseIdInIDatabaseObject =
            new Lazy<string>(() => ExpressionExtensions.GetMemberName(() => new ClassForIdNameInIPersistableDatabase().DatabaseId));

        private static readonly Lazy<string> _databaseIdInIVisualProperties =
            new Lazy<string>(() => ExpressionExtensions.GetMemberName(() => new ClassForIdNameInIVisualProperties().BelongsToDatabaseId));

        public string DatabaseIdInIDatabaseObject
        {
            get { return _databaseIdInIDatabaseObject.Value; }
        }

        public string DatabaseIdInIVisualProperties
        {
            get { return _databaseIdInIVisualProperties.Value; }
        }

        #region Nested type: ClassForIdNameInIObjectDatabase

        private class ClassForIdNameInIPersistableDatabase : IPersistable
        {
            #region Implementation of IDatabaseObject

            public long DatabaseId { get; set; }

            #endregion
        }

        #endregion

        #region Nested type: ClassForIdNameInIVisualProperties

        private sealed class ClassForIdNameInIVisualProperties : IVisualProperties
        {
            #region Implementation of IVisualProperties

            public string ViewIndex { get; set; }
            public string TemplateId { get; set; }
            public double X { get; set; }
            public double Y { get; set; }
            public double Width { get; set; }
            public double Height { get; set; }
            public double Opacity { get; set; }
            public int ZIndex { get; set; }
            public double Angle { get; set; }
            public int ViewId { get; set; }

            #endregion

            #region Implementation of IVisualProperties

            public long BelongsToDatabaseId { get; set; }

            #endregion

            public event PropertyChangedEventHandler PropertyChanged;

            [NotifyPropertyChangedInvocator]
            private void OnPropertyChanged(string propertyName)
            {
                var handler = this.PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }

        #endregion

        #endregion

        #endregion

        #region Path Namespace

        /// <summary>
        ///     XML Namespace der für den Pfad in der Xml-Db verwendet wird.
        ///     Pfad in Xml-Db = Namespace der Klasse -> muss mitgesandt werden damit Object deserialisiert werden kann
        /// </summary>
        public string PathNamespace { get; set; }

        internal string GetPathNamespaceDeclaration(string prefix)
        {
            return string.Format("declare namespace {0} = \"{1}\";\n", prefix, PathNamespace);
        }

        internal XElement AddAttributeToXElementWithPathNamespace(XElement xml, string attributeName, object content)
        {
            var ns = XNamespace.Get(PathNamespace);
            var prefix = new XAttribute(XNamespace.Xmlns + "zoil", ns);
            var att = new XAttribute(ns + attributeName, content);

            xml.Add(prefix);
            xml.Add(att);

            return xml;
        }

        internal string ReadAttributeValueWithPathNamespace(XElement xml, string attributeName)
        {
            var ns = XNamespace.Get(PathNamespace);
            return xml.Attribute(ns + attributeName).Value;
        }

        internal XNamespace GetPathNamespace()
        {
            return XNamespace.Get(PathNamespace);
        }

        #endregion

        #region Serialization

        public ISerializer Serializer { get; set; }

        #endregion

        #region Add Remote Object

        //TODO: auf Event umändern
        /// <summary>
        ///     Condition function to accept new remotely created objects
        ///     Return <c>true</c> if object should be accepted, otherwise <c>false</c>
        ///     If accepted the <see cref="Database.ObjectRemotelyCreated" /> event is raised
        /// </summary>
        public Func<DatabaseObjectActionEventArgs, bool> AcceptRemotelyCreatedObjectCondition { get; set; }

        #endregion

        #region Store

        public TimeSpan CommitToDatabaseThrottleInterval { get; set; }

        #endregion

        //TODO: ausmisten

        public Database ConnectToDatabase()
        {
            return new Database(this);
        }
    }
}